package loadgen

import (
	"bingfabianchengshizhan/cmd/4.21-loadgen/lib"
	"bingfabianchengshizhan/cmd/helper/log"
	"bytes"
	"context"
	"errors"
	"golang.org/x/tools/blog/atom"
	"math"
	"sync/atomic"
	"time"
	"fmt"
)

// 日志记录器
var logger = log.DLogger()

// myGenerator 代表荷载发生器的实现类型
type myGenerator struct {
	caller lib.Caller // 调用器
	timoutNS time.Duration // 处理超时时间，单位：纳秒
	lps uint32 // 每秒荷载量
	durationNS time.Duration // 荷载持续时间，单位：纳秒
	concurrency uint32 // 荷载并发量
	tickets lib.GoTickets // Gortinue票池
	ctx context.Context // 上下文
	cancelFunc context.CancelFunc // 取消函数
	callCount int64 // 调用计数
	status uint32 // 状态
	resultCh chan *lib.CallResult // 调用结果通道
}

// NewGenerator 会仙剑一个荷载发生器
func NewGenerator(pset ParamSet) (lib.Generator, error) {
	logger.Infoln("New a load generator...")
	if err:=pset.Check();err!=nil{
		return nil,err
	}
	gen:=&myGenerator{
		caller: pset.Caller,
		timoutNS:pset.TimeoutNS,
		lps:pset.LPS,
		durationNS:pset.DurationNS,
		status:lib.STATUS_ORIGINAL,
		resultCh:pset.ResultCh,
	}
	if err:=gen.init();err!=nil{
		return nil,err
	}
	return gen,nil
}

// 初始化荷载发生器
func (gen *myGenerator) init() error {
	var buf bytes.Buffer
	buf.WriteString("Initialzing the load generator...")
	// 荷载的并发量 = 荷载的响应超时时间 / 荷载发送的时间间隔
	var total64 = int64(gen.timoutNS)/int64(1e9/gen.lps)+1
	if total64 > math.MaxInt32{
		total64= math.MaxInt32
	}
	gen.concurrency=uint32(total64)
	tickets,err:=lib.NewGoTickets(gen.concurrency)
	if err!=nil{
		return err
	}
	gen.tickets=tickets
	buf.WriteString(fmt.Sprintf("Done. (concurrency=%d)", gen.concurrency))
	logger.Infoln(buf.String())
	return nil
}

// callOne 会想荷载承受方发起一次调用
func (gen *myGenerator) callOne(rawReq *lib.RawReq) *lib.RawResp {
	atomic.AddInt64(&gen.callCount,1)
	if rawReq == nil{
		return &lib.RawResp{
			ID:-1,
			Err:errors.New("Invalid raw request."),
		}
	}
	start:=time.Now().UnixNano()
	resp,err:=gen.caller.Call(rawReq.Req,gen.timoutNS)
	end:=time.Now().UnixNano()
	elapsedTime:=time.Duration(end-start)
	var rawResp lib.RawResp
	if err!=nil{
		errMsg:=fmt.Sprintf("Sync Call Error: %s.", err)
		rawResp=lib.RawResp{
			ID:rawReq.ID,
			Err:errors.New(errMsg),
			Elapse:elapsedTime,
		}
	}else {
		rawResp=lib.RawResp{
			ID:     rawReq.ID,
			Resp:   resp,
			Elapse: elapsedTime,
		}
	}
	return  &rawResp
}

// asyncSend 会异步地调用承受方的接口
func (gen *myGenerator) asyncCall() {
	gen.tickets.Take()
	go func() {
		defer func() {
			if p:=recover();p!=nil{
				err,ok:=interface{}(p).(error)
				var errMsg string
				if ok {
					errMsg=fmt.Sprintf("Async Call Panic! (error: %s)", err)
				}else {
					errMsg=fmt.Sprintf("Async Call Panic! (clue: %#v)", p)
				}
				logger.Errorln(errMsg)
				result:=&lib.CallResult{
					ID:-1,
					Code:lib.RET_CODE_FATAL_CALL,
					Msg:errMsg,
				}
				gen.sendResult
			}
		}()
	}()
}

// sendResult 用于发送调用结果
func (gen *myGenerator) sendResult(result *lib.CallResult) bool {
	if atomic.LoadInt32(&gen.status)!=lib.STATUS_STARTED{
		gen
	}
}


